<!DOCTYPE HTML>
<html>

<head>
  <title>Joey's Profile</title>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
  <style type="text/css">
    body {
      font-family: Helvetica,Arial,"Microsoft YaHei","黑体","宋体",sans-serif;;
      background-color: #fff;
      text-shadow: rgb(117, 117, 117) 1px 0 2px;
      margin: 0;
      padding: 0;
      overflow: hidden;
    }

    #info {
      position: absolute;
      font-size: 1.2em;
      color: #BCC;
      top: 10px;
      width: calc(100% - 2em);
      text-align: left;
      z-index: 100;
      display: block;
    }

    .detail {
      position: absolute;
      top: 170px;
      left: 2em;
      line-height: 1.5;
      overflow-y: scroll;
      min-width: 95%;
      max-width: 95%;
      height: calc(100% - 50px);
      max-height: calc(100% - 50px);
      word-break: break-all;
    }
    .detail::-webkit-scrollbar {
      width:15px;
    }
    .detail::-webkit-scrollbar * {
      background:transparent;
    }
    .detail::-webkit-scrollbar-thumb {
      background:rgba(0, 164, 170, 0.1) !important;
    }
    .detail a {
      text-decoration: unset;
      color: rgb(142, 207, 253);
    }
    h3 {
      margin: 0.6em 0 0.6em 0;
      font-weight: 300;
      font-size: 1.5em;
    }

    #qr {
      position: absolute;
      top: 4em;
      opacity: 0;
      z-index: 99999;
      -webkit-transition: opacity 1s ease-in;
      -moz-transition: opacity 1s ease-in;
      -o-transition: opacity 1s ease-in;
      -ms-transition: opacity 1s ease-in;
      transition: opacity 1s ease-in;
    }
  </style>
</head>

<body>
  <div id="info">
    <canvas id="particle2d">
    </canvas>
    <div class="detail">
    </div>
    <div>
      <img id="qr" onclick="hideQr()" src="http://filecdn.code2life.top/qrcode.jpg" />
    </div>
  </div>
  <script src="http://cdn.bootcss.com/three.js/r83/three.min.js"></script>
  <script src="http://filecdn.code2life.top/three.proton.min.js"></script>
  <script src="http://filecdn.code2life.top/proton.min.js"></script>
  <script>
    var proton, proton2d, emitter2d, emitter1, emitter2, R, canvas;
    var camera, scene, renderer, renderer2d, clock, spring, controls;
    var textIndex = 0;
    var text = 'Joey Yang：软件工程师$sp$ 个人博客: $home$$br$$work$$sp$2015年7月~2016年1月，实习于东方海外（OOCL），参与AngularJS、Node.js开发$br$$sp$2017年3月~2016年4月，就职于科大讯飞教育业务部门，承担多个系统前后端开发工作，包括服务端（Java SSM、ASP.Net MVC、Node.js & MySQL、Oracle、MongoDB）, 客户端C++, C# (WinForm/WPF/CEF), NW.js, 以及前端AngularJS、Vue的开发。期间负责的其中两个系统服务端的性能优化工作，关键业务性能提升近十倍。$br$$sp$2018年4月后，入职软视软件（ZOOM），参与会议室相关的业务开发。$br$$sp$$sp$2018年解决了某重量级Node.js系统的大量可用性及性能相关问题（进程崩溃，CPU占用率过高，内存泄漏，缓存，限流，负载均衡等等）并用Typescript和RxJS对其中一个实时通信组件重写$br$$sp$$sp$2018年11月左右，兼职负责业务拆分及微服务转型的DevOps方案落地，构建Kubernetes集群，Prometheus监控告警平台，SpringCloud项目的CI/CD流水线，Docker基础镜像以及容器编排。同时构建并标准化了新的前后端分离Vue项目的CI/CD流程。$br$$sp$$sp$2019年5月后，负责其中一个子业务进行SpringCloud微服务化重构，参与系统之间的数据同步和迁移工作等等$int$$sp$2016年毕业于南京大学软件工程专业，长期参与一线开发工作，喜欢尝试新技术，读源码思考原理。JS/TS比较熟练，Node.js略精；在云原生技术栈方面，对Golang、Linux容器、Kubernetes、Service Mesh、DevOps有一些实践经验和浓厚的兴趣；Java技术栈虽然熟悉但不喜欢。业余时间喜欢捯饬单片机和单板计算机，学习探索物联网领域。发布有技术类微信公众号 "Anti丨Entropy"，这个名字是我所理解的生命的意义，即制造反熵（Anti-Entropy）。$scan$';
    var contentMap = {
      'br': '<br />',
      'home': '<a href="http://code2life.top">http://code2life.top</a>',
      'sp': '<span>&nbsp;&nbsp;&nbsp;&nbsp;</span>',
      'work': '<h3>工作经历</h3>',
      'int': '<h3>关于我</h3>',
      'scan': '<a href="javascript:void(0)" onclick="showQR()">点击扫码关注😏</a>'
    }

    var contentEle = document.querySelector('.detail');
    var qrCode = document.querySelector('#qr');
    if (window.innerWidth <= 768) {
      qrCode.style.width = window.innerWidth / 4 + 'px';
      qrCode.style.left = window.innerWidth / 2 - (window.innerWidth / 8) + 'px';
      qrCode.style.top = window.innerHeight / 2 - (window.innerWidth / 8) + 'px';
    } else {
      qrCode.style.width = window.innerWidth / 2 + 'px';
      qrCode.style.left = window.innerWidth / 4 + 'px';
      qrCode.style.top = window.innerHeight / 4 + 'px';
    }

    function showQR() {
      qrCode.style.opacity = '0.62';
    }

    function hideQr() {
      qrCode.style.opacity = '0';
    }

    init();
    function init() {
      addScene();
      addLights();
      addStars();
      addProton();
      animate();
      addProton2d();
    }

    function addProton2d() {
      canvas = document.querySelector('#particle2d');
      canvas.width = window.innerWidth;
      canvas.height = window.innerHeight;
      context = canvas.getContext('2d');
      context.globalCompositeOperation = "lighter";
      loadImage();
    }

    function loadImage() {
      var image = new Image();
      image.onload = function (e) {
        var minWidth = 768;
        var calWidth = e.target.width;
        var calHeight = e.target.height;
        if (window.innerWidth <= minWidth) {
          calWidth = window.innerWidth - 90;
          calHeight = calWidth * (e.target.height / e.target.width);
        }
        var rect = new Proton2D.Rectangle((canvas.width - calWidth) / 2, (canvas.height - calHeight) / 2, calWidth, calHeight);
        context.drawImage(e.target, rect.x, rect.y, calWidth, calHeight);
        createProton(rect);

        contentEle.style.top = calHeight + 40 + 'px';
        contentEle.style.maxHeight = window.innerHeight - (calHeight + 40 + 50) + 'px';
        if (detectIEEdge()) {
          contentEle.style.overflowY = 'hidden';
          addText();
        } else {
          addText();
        }
        tick();
      }
      image.src = 'assets/logo2.png';
    }

    function tick() {
      requestAnimationFrame(tick);
      proton2d.update();
    }

    function addText () {
      if (textIndex >= text.length) {
        return;
      }
      requestAnimationFrame(function() {
        var toAppend = text[textIndex];
        var node;
        if (toAppend == '$') {
          var mapKey = '';
          textIndex++;
          while (text[textIndex] != '$' || textIndex >= text.length) {
            mapKey += text[textIndex];
            textIndex++;
          }
          // var div = document.createElement('div');
          // div.innerHTML = contentMap[mapKey];
          // node = div.firstChild;
          contentEle.insertAdjacentHTML('beforeend', contentMap[mapKey]);
        } else {
          // node = document.createTextNode(text[textIndex]);
          contentEle.insertAdjacentText('beforeend', text[textIndex]);
        }
        // contentEle.appendChild(node);
        textIndex++;
        contentEle.scrollTop = contentEle.scrollHeight;
        addText();
      });
    }

    function createProton(rect) {
      proton2d = new Proton2D;
      emitter2d = new Proton2D.Emitter();
      emitter2d.rate = new Proton2D.Rate(new Proton2D.Span(35, 45), new Proton2D.Span(.04));
      emitter2d.addInitialize(new Proton2D.Position(new Proton2D.PointZone(0, 0)));
      emitter2d.addInitialize(new Proton2D.Mass(1));
      emitter2d.addInitialize(new Proton2D.Radius(1.5, 2.5));
      emitter2d.addInitialize(new Proton2D.Life(2));
      var imagedata = context.getImageData(rect.x, rect.y, rect.width, rect.height);
      emitter2d.addInitialize(new Proton2D.P(new Proton2D.ImageZone(imagedata, 30, 20)));
      randomBehaviour = new Proton2D.RandomDrift(0.2, 0.1, .1);
      gravity = new Proton2D.Gravity(0.07);
      emitter2d.addBehaviour(customScaleBehaviour());
      emitter2d.addBehaviour(gravity);
      emitter2d.addBehaviour(randomBehaviour);
      emitter2d.addBehaviour(new Proton2D.Color(['#00aeff', '#0fa954', '#54396e', '#e61d5f']));
      emitter2d.addBehaviour(new Proton2D.CrossZone(new Proton2D.RectZone(0, 0, canvas.width, canvas.height), 'collision'));
      emitter2d.emit();
      proton2d.addEmitter(emitter2d);
      renderer2d = new Proton2D.CanvasRenderer(canvas);
      proton2d.addRenderer(renderer2d);
      index = 0;
      window.addEventListener('mousedown', function (e) {
        index++;
        if (index % 3 == 1) {
          randomBehaviour.reset(20, 20, .5);
          gravity.reset(1.5);
        } else if (index % 3 == 2) {
          emitter2d.p.x = rect.x;
          emitter2d.p.y = rect.y;
          randomBehaviour.reset(50, 50, .1);
          gravity.reset(0);
        } else {
          emitter2d.p.x = 30;
          emitter2d.p.y = 0;
          randomBehaviour.reset(0.2, 0.1, .1);
          gravity.reset(0.07);
        }
    });
    }



    function customScaleBehaviour() {
      return {
        initialize: function (particle) {
          particle.oldRadius = particle.radius;
          particle.scale = 0;
        },
        applyBehaviour: function (particle) {
          if (particle.energy >= 2 / 3) {
            particle.scale = (1 - particle.energy) * 3;
          } else if (particle.energy <= 1 / 3) {
            particle.scale = particle.energy * 3;
          }
          particle.radius = particle.oldRadius * particle.scale;
        }
      }
    }


    // 3d background
    var ctha = 0;
    var tha = 0;
    var r = 300;
    scene.position.y = -200;
    scene.position.z = 200;
    scene.position.x = -200;

    function addScene() {
      camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 1000);
      camera.position.y = 500;
      scene = new THREE.Scene();
      scene.fog = new THREE.Fog(0xffffff, 1, 10000);

      renderer = new THREE.WebGLRenderer();
      renderer.setPixelRatio(window.devicePixelRatio);
      renderer.setSize(window.innerWidth, window.innerHeight);
      document.body.appendChild(renderer.domElement);
      window.addEventListener('resize', onWindowResize, false);
    }

    function addLights() {
      var ambientLight = new THREE.AmbientLight(0x101010);
      scene.add(ambientLight);

      var pointLight = new THREE.PointLight(0xffffff, 2, 1000, 1);
      pointLight.position.set(0, 200, 200);
      scene.add(pointLight);
    }

    function addStars() {
      var geometry = new THREE.Geometry();
      for (var i = 0; i < 10000; i++) {
        var vertex = new THREE.Vector3();
        vertex.x = THREE.Math.randFloatSpread(2000);
        vertex.y = THREE.Math.randFloatSpread(2000);
        vertex.z = THREE.Math.randFloatSpread(2000);
        geometry.vertices.push(vertex);
      }
      var particles = new THREE.Points(geometry, new THREE.PointsMaterial({
        color: 0x888888
      }));
      scene.add(particles);
    }

    function addProton() {
      proton = new Proton();
      R = 50;
      emitter1 = createEmitter(R, 0, '#4F1500', '#0029FF');
      emitter2 = createEmitter(-R, 0, '#004CFE', '#6600FF');

      proton.addEmitter(emitter1);
      proton.addEmitter(emitter2);
      proton.addRender(new Proton.SpriteRender(scene));
    }

    function createEmitter(x, y, color1, color2) {
      var emitter = new Proton.Emitter();
      emitter.rate = new Proton.Rate(new Proton.Span(5, 7), new Proton.Span(.01, .02));
      emitter.addInitialize(new Proton.Mass(1));
      emitter.addInitialize(new Proton.Life(2));
      emitter.addInitialize(new Proton.Body(createSprite()));
      emitter.addInitialize(new Proton.Radius(22));
      emitter.addInitialize(new Proton.V(250, new Proton.Vector3D(0, 0, -1), 0));

      emitter.addBehaviour(new Proton.Alpha(1, 0));
      emitter.addBehaviour(new Proton.Color(color1, color2));
      emitter.addBehaviour(new Proton.Scale(1, 0.5));

      emitter.p.x = x;
      emitter.p.y = y;
      emitter.emit();
      return emitter;
    }

    function createSprite() {
      var map = new THREE.TextureLoader().load("http://filecdn.code2life.top/dot.png");
      var material = new THREE.SpriteMaterial({
        map: map,
        color: 0xff0000,
        blending: THREE.AdditiveBlending,
        fog: true
      });
      return new THREE.Sprite(material);
    }

    function animate() {
      requestAnimationFrame(animate);
      animateEmitter();
      render();
    }

    function render() {
      proton.update();
      renderer.render(scene, camera);
      camera.lookAt(scene.position);
      rotateCamera(camera);
    }

    function animateEmitter() {
      tha += .13;
      emitter1.p.x = R * Math.cos(tha);
      emitter1.p.y = R * Math.sin(tha);

      emitter2.p.x = R * Math.cos(tha + Math.PI / 2);
      emitter2.p.y = R * Math.sin(tha + Math.PI / 2);
    }

    function rotateCamera(camera) {
      ctha += .016;
      camera.position.x = Math.sin(ctha) * r;
      camera.position.y = Math.cos(ctha) * r;
    }

    function onWindowResize() {
    }

    function detectIEEdge() {
    var ua = window.navigator.userAgent;

    var msie = ua.indexOf('MSIE ');
    if (msie > 0) {
        return parseInt(ua.substring(msie + 5, ua.indexOf('.', msie)), 10);
    }

    var trident = ua.indexOf('Trident/');
    if (trident > 0) {
        var rv = ua.indexOf('rv:');
        return parseInt(ua.substring(rv + 3, ua.indexOf('.', rv)), 10);
    }

    var edge = ua.indexOf('Edge/');
    if (edge > 0) {
       return parseInt(ua.substring(edge + 5, ua.indexOf('.', edge)), 10);
    }
    return false;
}
  </script>
</body>

</html>